﻿using System;
//using System.Collections.Generic;
//using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace DuinoWebserver.NetworkTime
{
    public class NtpClient
    {
        public static TimeSpan UTC_Offset = new TimeSpan(2, 0, 0);

        public static DateTime GetNetworkTime()
        {
            return GetNetworkTime("time-a.nist.gov");
        }

        public static DateTime GetNetworkTime(string ntpServer)
        {
            IPAddress[] address = Dns.GetHostEntry(ntpServer).AddressList;

            if (address == null || address.Length == 0)
            {
                throw new ArgumentException("Couldn not resolve Time Server " + ntpServer + ".", "ntpServer");
            }

            IPEndPoint ep = new IPEndPoint(address[0], 123);

            return GetNetworkTime(ep);
        }

        public static DateTime GetNetworkTime(EndPoint ep)
        {
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            s.Connect(ep);

            byte[] ntpData = new byte[48]; // RFC 2030 
            ntpData[0] = 0x1B;
            for (int i = 1; i < 48; i++)
                ntpData[i] = 0;

            s.Send(ntpData);
            s.Receive(ntpData);

            byte offsetTransmitTime = 40;
            ulong intpart = 0;
            ulong fractpart = 0;

            for (int i = 0; i <= 3; i++)
                intpart = 256 * intpart + ntpData[offsetTransmitTime + i];

            for (int i = 4; i <= 7; i++)
                fractpart = 256 * fractpart + ntpData[offsetTransmitTime + i];

            ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);
            s.Close();

            TimeSpan timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);

            DateTime dateTime = new DateTime(1900, 1, 1);
            dateTime += timeSpan;

            TimeSpan offsetAmount = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
            DateTime networkDateTime = (dateTime + offsetAmount);

            return networkDateTime;
        }

        public static bool TryGetNetworkTime(string ntpServer, out DateTime dt)
        {
            try
            {
                IPAddress[] address = Dns.GetHostEntry(ntpServer).AddressList;

                if (address == null || address.Length == 0)
                {
                    throw new ArgumentException("Couldn not resolve Time Server " + ntpServer + ".", "ntpServer");
                }

                IPEndPoint ep = new IPEndPoint(address[0], 123);
                Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                s.Connect(ep);

                byte[] ntpData = new byte[48]; // RFC 2030 
                ntpData[0] = 0x1B;
                for (int i = 1; i < 48; i++)
                    ntpData[i] = 0;

                s.SendTimeout = 1000;
                s.Send(ntpData);
                s.ReceiveTimeout = 1000;
                s.Receive(ntpData);

                byte offsetTransmitTime = 40;
                ulong intpart = 0;
                ulong fractpart = 0;

                for (int i = 0; i <= 3; i++)
                    intpart = 256 * intpart + ntpData[offsetTransmitTime + i];

                for (int i = 4; i <= 7; i++)
                    fractpart = 256 * fractpart + ntpData[offsetTransmitTime + i];

                ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);
                s.Close();

                TimeSpan timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);

                DateTime dateTime = new DateTime(1900, 1, 1);
                dateTime += timeSpan;

//                TimeSpan offsetAmount = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); // on Netduino null
                DateTime networkDateTime = (dateTime + UTC_Offset);// offsetAmount);// 

                dt = networkDateTime;
                Application.DebugPrint("NtpClient:  " + dt + "   " + (dateTime + UTC_Offset));// + "\n TimeZone: " + offsetAmount.ToString());
                return true;
            }
            catch (Exception ex)
            {
                Application.DebugPrint("Exception at NtpClient:  " + ex.Message + ex.StackTrace);
                
                dt = DateTime.MinValue;
                return false;
            }
        }
    }
}
